﻿using MvvmCross.Platform;
using MvvmCross.Plugins.Messenger;
using MvvmCross.Core.ViewModels;
using Sidvall.AuthorizationManager.UI.Entities;
using Sidvall.Data;
using Sidvall.IdentityManager.UI.Messaging;
using Sidvall.Serialization;
using System.Threading.Tasks;
using System.Windows.Input;

namespace Sidvall.AuthorizationManager.UI.ViewModels
{
    public class UserViewModel : Sidvall.IdentityManager.UI.ViewModels.PluginViewModelBase
    {
        #region Public Members

        #region UserId

        private string _UserId;
        public string UserId
        {
            get
            {
                return _UserId;
            }
            set
            {
                if (_UserId == value)
                    return;
                _UserId = value;
                RaisePropertyChanged("UserId");
            }
        }

        #endregion
        #region AuthorizationScopes

        private AuthorizationScopeCollection _AuthorizationScopes;
        public AuthorizationScopeCollection AuthorizationScopes
        {
            get
            {
                return _AuthorizationScopes;
            }
            private set
            {
                _AuthorizationScopes = value;
                RaisePropertyChanged("AuthorizationScopes");
            }
        }

        #endregion
        #region ActiveAuthorizationScope

        private AuthorizationScope _ActiveAuthorizationScope;
        public AuthorizationScope ActiveAuthorizationScope
        {
            get
            {
                return _ActiveAuthorizationScope;
            }
            set
            {
                if (_ActiveAuthorizationScope != null)
                    _ActiveAuthorizationScope.ChildChanged -= OnActiveAuthorizationScopeChildChanged;
                if (_ActiveAuthorizationScope != null)
                    _ActiveAuthorizationScope.PropertyChanged -= OnActiveAuthorizationScopePropertyChanged;
                _ActiveAuthorizationScope = value;
                if (_ActiveAuthorizationScope != null)
                    _ActiveAuthorizationScope.ChildChanged += OnActiveAuthorizationScopeChildChanged;
                if (_ActiveAuthorizationScope != null)
                    _ActiveAuthorizationScope.PropertyChanged += OnActiveAuthorizationScopePropertyChanged;
                RaisePropertyChanged("ActiveAuthorizationScope");
                RaisePropertyChanged("ActiveAuthorizationScopeUser");
                RaisePropertyChanged("Title");
                this.ActiveUser = this.ActiveAuthorizationScopeUser;
            }
        }

        #endregion
        #region ActiveAuthorizationScopeUser

        public AuthorizationScopeUser ActiveAuthorizationScopeUser
        {
            get
            {
                if (this.ActiveAuthorizationScope == null)
                    return null;
                if (this.ActiveAuthorizationScope.AuthorizationScopeUsers == null)
                    return null;
                if (this.ActiveAuthorizationScope.AuthorizationScopeUsers.Count != 1)
                    return null;
                return this.ActiveAuthorizationScope.AuthorizationScopeUsers[0];
            }
        }

        #endregion
        #region ActiveUser

        private AuthorizationScopeUser _ActiveUser;
        public AuthorizationScopeUser ActiveUser
        {
            get
            {
                return _ActiveUser;
            }
            set
            {
                _ActiveUser = value;
                RaisePropertyChanged(nameof(ActiveUser));
                RaisePropertyChanged(nameof(CanEditUser));
                InitializeActiveUser();
            }
        }

        #endregion
        #region ActiveUserClaims

        private ClaimCollection _ActiveUserClaims;
        public ClaimCollection ActiveUserClaims
        {
            get
            {
                return _ActiveUserClaims;
            }
            private set
            {
                if (_ActiveUserClaims != null)
                    _ActiveUserClaims.ChildChanged -= OnActiveUserClaimsChildChanged;
                _ActiveUserClaims = value;
                if (_ActiveUserClaims != null)
                    _ActiveUserClaims.ChildChanged += OnActiveUserClaimsChildChanged;
                RaisePropertyChanged("ActiveUserClaims");
            }
        }

        #endregion
        #region ActiveUserContext

        private string _ActiveUserContext;
        public string ActiveUserContext
        {
            get
            {
                return _ActiveUserContext;
            }
            set
            {
                string formattedValue;
                try
                {
                    var item = Sidvall.Serialization.SerializerManager.Current.Deserialize(value);
                    if (item != null)
                        formattedValue = Sidvall.Serialization.SerializerManager.Current.SerializeObject(item, Serialization.Formatting.Indented);
                    else
                        formattedValue = value;
                }
                catch
                {
                    formattedValue = value;
                }
                if (_ActiveUserContext == formattedValue)
                    return;
                _ActiveUserContext = formattedValue;
                RaisePropertyChanged("ActiveUserContext");
                if (this.ActiveUser != null)
                    this.ActiveUser.UserContext = value;
            }
        }

        #endregion
        #region ActiveResourceAction

        private AuthorizationScopeResourceAction _ActiveResourceAction;
        public AuthorizationScopeResourceAction ActiveResourceAction
        {
            get
            {
                return _ActiveResourceAction;
            }
            set
            {
                _ActiveResourceAction = value;
                RaisePropertyChanged("ActiveResourceAction");
            }
        }

        #endregion
        #region ValidUserClaims

        private Sidvall.Security.ClaimCollection _ValidUserClaims;
        public Sidvall.Security.ClaimCollection ValidUserClaims
        {
            get
            {
                if (_ValidUserClaims == null)
                    this.ValidUserClaims = new Sidvall.Security.ClaimCollection();
                return _ValidUserClaims;
            }
            set
            {
                _ValidUserClaims = value;
                RaisePropertyChanged("ValidUserClaims");
            }
        }

        #endregion
        #region Title

        public string Title
        {
            get
            {
                if (this.ActiveAuthorizationScope == null)
                    return string.Empty;
                return this.ActiveAuthorizationScope.Name;
            }
        }

        #endregion
        #region CanEditUser

        public bool CanEditUser
        {
            get
            {
                if (this.ActiveUser == null)
                    return false;
                return true;
            }
        }

        #endregion
        #region MissingUserPolicies

        private Sidvall.Data.KeyValueCollection<string, Data.MissingUserPolicy> _MissingUserPolicies;
        public Sidvall.Data.KeyValueCollection<string, Data.MissingUserPolicy> MissingUserPolicies
        {
            get
            {
                if (_MissingUserPolicies == null)
                    this.MissingUserPolicies = Sidvall.Data.KeyValueManager.FromEnum<Data.MissingUserPolicy>();
                return _MissingUserPolicies;
            }
            set
            {
                _MissingUserPolicies = value;
                RaisePropertyChanged("MissingUserPolicies");
            }
        }

        #endregion
        #region ResourceItemActionPolicies

        private Sidvall.Data.KeyValueCollection<string, Sidvall.AuthorizationManager.Data.ResourceItemActionPolicy> _ResourceItemActionPolicies;
        public Sidvall.Data.KeyValueCollection<string, Sidvall.AuthorizationManager.Data.ResourceItemActionPolicy> ResourceItemActionPolicies
        {
            get
            {
                if (_ResourceItemActionPolicies == null)
                    this.ResourceItemActionPolicies = Sidvall.Data.KeyValueManager.FromEnum<Sidvall.AuthorizationManager.Data.ResourceItemActionPolicy>();
                return _ResourceItemActionPolicies;
            }
            set
            {
                _ResourceItemActionPolicies = value;
                RaisePropertyChanged("ResourceItemActionPolicies");
            }
        }

        #endregion
        #region AuthorizationScopeAccessLevels

        private Sidvall.Data.KeyValueCollection<string, Data.AuthorizationScopeAccessLevel> _AuthorizationScopeAccessLevels;
        public Sidvall.Data.KeyValueCollection<string, Data.AuthorizationScopeAccessLevel> AuthorizationScopeAccessLevels
        {
            get
            {
                if (_AuthorizationScopeAccessLevels == null)
                    this.AuthorizationScopeAccessLevels = Sidvall.Data.KeyValueManager.FromEnum<Data.AuthorizationScopeAccessLevel>();
                return _AuthorizationScopeAccessLevels;
            }
            set
            {
                _AuthorizationScopeAccessLevels = value;
                RaisePropertyChanged("AuthorizationScopeAccessLevels");
            }
        }

        #endregion
        #region ResourceActionOptions

        private ActionOptionCollection _ResourceActionOptions;
        public ActionOptionCollection ResourceActionOptions
        {
            get
            {
                return _ResourceActionOptions;
            }
            set
            {
                _ResourceActionOptions = value;
                RaisePropertyChanged("ResourceActionOptions");
            }
        }

        #endregion
        #region ResourceAccessLevels

        private Sidvall.Data.KeyValueCollection<string, Data.ResourceAccessLevel> _ResourceAccessLevels;
        public Sidvall.Data.KeyValueCollection<string, Data.ResourceAccessLevel> ResourceAccessLevels
        {
            get
            {
                if (_ResourceAccessLevels == null)
                    this.ResourceAccessLevels = Sidvall.Data.KeyValueManager.FromEnum<Data.ResourceAccessLevel>();
                return _ResourceAccessLevels;
            }
            set
            {
                _ResourceAccessLevels = value;
                RaisePropertyChanged("ResourceAccessLevels");
            }
        }

        #endregion
        #region ComparisonOperators

        private Sidvall.Data.KeyValueCollection<string, Sidvall.Data.ComparisonOperator> _ComparisonOperators;
        public Sidvall.Data.KeyValueCollection<string, Sidvall.Data.ComparisonOperator> ComparisonOperators
        {
            get
            {
                if (_ComparisonOperators == null)
                    this.ComparisonOperators = Sidvall.Data.KeyValueManager.FromEnum<Sidvall.Data.ComparisonOperator>();
                return _ComparisonOperators;
            }
            set
            {
                _ComparisonOperators = value;
                RaisePropertyChanged("ComparisonOperators");
            }
        }

        #endregion
        #region ValidClaims

        private Sidvall.Security.ClaimCollection _ValidClaims;
        public Sidvall.Security.ClaimCollection ValidClaims
        {
            get
            {
                if (_ValidClaims == null)
                    this.ValidClaims = new Sidvall.Security.ClaimCollection();
                return _ValidClaims;
            }
            set
            {
                _ValidClaims = value;
                RaisePropertyChanged("ValidClaims");
            }
        }

        #endregion
        #region ResourceActionListIsVisible

        private bool _ResourceActionListIsVisible = true;
        public bool ResourceActionListIsVisible
        {
            get
            {
                return _ResourceActionListIsVisible;
            }
            set
            {
                _ResourceActionListIsVisible = value;
                RaisePropertyChanged("ResourceActionListIsVisible");
            }
        }

        #endregion

        // Resource Actions
        #region AddResourceActionCommand

        public ICommand AddResourceActionCommand
        {
            get
            {
                return new MvxCommand(AddResourceAction);
            }
        }

        #endregion
        #region AddResourceAction

        public void AddResourceAction()
        {
            if (this.ActiveAuthorizationScope == null)
                return;

            var item = new AuthorizationScopeResourceAction()
            {
                AccessLevel = Data.ResourceAccessLevel.Public,
                IsEnabled = true,
                ValidForResourceActionItems = true,
            };
            item.LoadAuthorizationScopeResourceActionClaims(new AuthorizationScopeResourceActionClaimCollection());
            if (this.ActiveAuthorizationScope.AuthorizationScopeId != null)
                item.AuthorizationScopeId = this.ActiveAuthorizationScope.AuthorizationScopeId.Value;
            this.ActiveAuthorizationScope.AuthorizationScopeResourceActions.Add(item);
            this.ActiveResourceAction = item;
        }

        #endregion
        #region DeleteResourceActionCommand

        public ICommand DeleteResourceActionCommand
        {
            get
            {
                return new MvxCommand<AuthorizationScopeResourceAction>(DeleteResourceAction);
            }
        }

        #endregion
        #region DeleteResourceAction

        public void DeleteResourceAction(AuthorizationScopeResourceAction item)
        {
            if (this.ActiveAuthorizationScope == null)
                return;
            if (item == null)
                return;
            this.ActiveAuthorizationScope.AuthorizationScopeResourceActions.Remove(item);
        }

        #endregion
        #region ShowResourceActionListCommand

        public ICommand ShowResourceActionListCommand
        {
            get
            {
                return new MvxCommand(ShowResourceActionList);
            }
        }

        #endregion
        #region ShowResourceActionList

        public void ShowResourceActionList()
        {
            this.ResourceActionListIsVisible = true;
        }

        #endregion
        #region AddClaimCommand

        public ICommand AddClaimCommand
        {
            get
            {
                return new MvxCommand(AddClaim);
            }
        }

        #endregion
        #region AddClaim

        public void AddClaim()
        {
            AuthorizationScopeResourceActionClaim item;

            if (this.ActiveResourceAction == null)
                return;

            item = new AuthorizationScopeResourceActionClaim()
            {
                IsEnabled = true,
            };
            if (this.ActiveResourceAction.AuthorizationScopeResourceActionId != null)
                item.AuthorizationScopeResourceActionId = this.ActiveResourceAction.AuthorizationScopeResourceActionId.Value;
            this.ActiveResourceAction.AuthorizationScopeResourceActionClaims.Add(item);
        }

        #endregion
        #region DeleteClaimCommand

        public ICommand DeleteClaimCommand
        {
            get
            {
                return new MvxCommand<AuthorizationScopeResourceActionClaim>(DeleteClaim);
            }
        }

        #endregion
        #region DeleteClaim

        public void DeleteClaim(AuthorizationScopeResourceActionClaim item)
        {
            if (this.ActiveResourceAction == null)
                return;
            if (item == null)
                return;
            this.ActiveResourceAction.AuthorizationScopeResourceActionClaims.Remove(item);
        }

        #endregion
        #region EditClaimsCommand

        public ICommand EditClaimsCommand
        {
            get
            {
                return new MvxCommand<AuthorizationScopeResourceAction>(EditClaims);
            }
        }

        #endregion
        #region EditClaims

        public void EditClaims(AuthorizationScopeResourceAction item)
        {
            if (this.ActiveAuthorizationScope == null)
                return;
            if (item == null)
                return;
            this.ActiveResourceAction = item;
            this.ResourceActionListIsVisible = false;
        }

        #endregion

        // User Claims
        #region AddUserClaimCommand

        public ICommand AddUserClaimCommand
        {
            get
            {
                return new MvxCommand(AddUserClaim);
            }
        }

        #endregion
        #region AddUserClaim

        public void AddUserClaim()
        {
            Claim item;

            if (this.ActiveUserClaims == null)
                return;

            item = new Claim()
            {
                ClaimType = Sidvall.Security.ClaimTypes.Role,
            };
            this.ActiveUserClaims.Add(item);
            OnUserClaimsChanged();
        }

        #endregion
        #region DeleteUserClaimCommand

        public ICommand DeleteUserClaimCommand
        {
            get
            {
                return new MvxCommand<Claim>(DeleteUserClaim);
            }
        }

        #endregion
        #region DeleteUserClaim

        public void DeleteUserClaim(Claim item)
        {
            if (this.ActiveUserClaims == null)
                return;
            if (item == null)
                return;
            this.ActiveUserClaims.Remove(item);
            OnUserClaimsChanged();
        }

        #endregion
        #region OnUserClaimsChanged

        public void OnUserClaimsChanged()
        {
            if (this.ActiveUserClaims == null)
                return;
            this.ActiveUser.UserClaims = this.ActiveUserClaims.Serialize();
        }

        #endregion

        // Authorization Scopes
        #region Select

        public ICommand SelectCommand
        {
            get
            {
                return new MvxCommand<AuthorizationScope>(async (o) => await SelectAsync(o));
            }
        }
        public async Task SelectAsync(AuthorizationScope item)
        {
            if (item == null)
                return;

            using (var busyManager = new BusyManager(this))
            {
                try
                {
                    if (item.IsSelected)
                    {
                        if (!item.HasUser)
                        {
                            item.AddUser(this.UserId);
                        }
                        await item.LoadAuthorizationScopeResourceActionsAsync();
                        await item.AuthorizationScopeResourceActions.LoadAuthorizationScopeResourceActionClaimsAsync();
                        this.ActiveAuthorizationScope = item;
                    }
                    else
                    {
                        if (item.HasUser)
                        {
                            if (this.ActiveUser != null)
                            {
                                if (this.ActiveUser.UserId == item.AuthorizationScopeUsers[0].UserId)
                                    this.ActiveAuthorizationScope = null;
                            }
                            item.AuthorizationScopeUsers.Clear();
                        }
                    }
                }
                catch (System.Exception ex)
                {
                    await Sidvall.UI.SystemContext.Current.DialogManager.ShowErrorMessageAsync("Error", ex);
                }
            }
        }

        #endregion
        #region Show

        public ICommand ShowCommand
        {
            get
            {
                return new MvxCommand<AuthorizationScope>(async (o) => await ShowAsync(o));
            }
        }
        public async Task ShowAsync(AuthorizationScope item)
        {
            if (item == null)
                return;

            using (var busyManager = new BusyManager(this))
            {
                try
                {
                    await item.LoadAuthorizationScopeResourceActionsAsync();
                    await item.AuthorizationScopeResourceActions.LoadAuthorizationScopeResourceActionClaimsAsync();
                    this.ActiveAuthorizationScope = item;
                }
                catch (System.Exception ex)
                {
                    await Sidvall.UI.SystemContext.Current.DialogManager.ShowErrorMessageAsync("Error", ex);
                }
            }
        }

        #endregion

        #region StartAsync

        protected override async Task StartAsync()
        {
            _ResourceActionListIsVisible = true;
            if (this.ContextParameter != null)
            {
                this.UserId = this.ContextParameter.UserId;
            }
            var messenger = Mvx.Resolve<IMvxMessenger>();
            this.ViewModelResources.Add(messenger.Subscribe<UserMessage>(OnUserMessageAsync));
            Sidvall.AuthorizationManager.Data.ClaimManager.Initialize(this.ValidClaims);
            Sidvall.AuthorizationManager.Data.ClaimManager.InitializeUserClaims(this.ValidUserClaims);

            this.ResourceActionOptions = ActionOptionCollection.NewResourceActionOptions();
            this.Scopes = await Sidvall.AuthorizationManager.UI.SystemContext.Current.DataPortal.AuthorizationScopeManager.GetItemsAsync();
            await InitializeUsersAsync();
            foreach (var authorizationScope in this.AuthorizationScopes)
            {
                if (authorizationScope.IsSelected)
                {
                    await SelectAsync(authorizationScope);
                    break;
                }
            }
        }

        #endregion

        #endregion
        #region Private Members

        private AuthorizationScopeCollection Scopes { get; set; }

        #region InitializeActiveUser

        private void InitializeActiveUser()
        {
            if (this.ActiveUser != null)
            {
                this.ActiveUserClaims = ClaimCollection.Deserialize(this.ActiveUser.UserClaims, new ClaimCollection());
                this.ActiveUserContext = this.ActiveUser.UserContext;
            }
            else
            {
                this.ActiveUserClaims = null;
                this.ActiveUserContext = null;
            }
        }

        #endregion
        #region InitializeUsersAsync

        private async Task InitializeUsersAsync()
        {
            AuthorizationScopeCollection scopes;
            AuthorizationScopeUserCollection users;

            scopes = this.Scopes.Copy(CopySettings.FullCopyAll(DataRowContextOption.State, null));
            if (!string.IsNullOrWhiteSpace(this.UserId))
                users = await Sidvall.AuthorizationManager.UI.SystemContext.Current.DataPortal.AuthorizationScopeUserManager.GetItemsByUserIdAsync(this.UserId);
            else
                users = null;
            scopes.LoadAuthorizationScopeUsers(users, true, true);
            this.AuthorizationScopes = scopes;
        }

        #endregion
        #region OnActiveAuthorizationScopeChildChanged

        private void OnActiveAuthorizationScopeChildChanged(object sender, Sidvall.Business.ChildChangedEventArgs e)
        {
            OnPluginMessage(IdentityManager.Data.PluginMessageType.UserModified);
        }

        #endregion
        #region OnActiveAuthorizationScopePropertyChanged

        private void OnActiveAuthorizationScopePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            OnPluginMessage(IdentityManager.Data.PluginMessageType.UserModified);
        }

        #endregion
        #region OnActiveUserClaimsChildChanged

        private void OnActiveUserClaimsChildChanged(object sender, Sidvall.Business.ChildChangedEventArgs e)
        {
            OnUserClaimsChanged();
        }

        #endregion
        #region OnUserMessageAsync

        private async void OnUserMessageAsync(UserMessage item)
        {
            Task task = null;
            switch (item.UserMessageType)
            {
                case Sidvall.IdentityManager.Data.UserMessageType.NewUser:
                    if (this.UserId.Equals(item.UserId, false, System.StringComparison.OrdinalIgnoreCase))
                        return;
                    this.UserId = item.UserId;
                    await InitializeUsersAsync();
                    break;
                case Sidvall.IdentityManager.Data.UserMessageType.UserSaved:
                    if (this.AuthorizationScopes == null)
                        return;
                    using (var busyManager = new BusyManager(this))
                    {
                        try
                        {
                            this.UserId = item.UserId;
                            this.AuthorizationScopes.SetUserId(this.UserId);
                            this.AuthorizationScopes = await this.AuthorizationScopes.SaveItemsAsync();
                        }
                        catch (System.Exception ex)
                        {
                            task = Sidvall.UI.SystemContext.Current.DialogManager.ShowErrorMessageAsync("Error", ex);
                        }
                        if (task != null)
                            await task;
                    }
                    break;
                default:
                    break;
            }
        }

        #endregion

        #endregion
    }
}
